﻿using System;
using System.Collections.Generic;
using System.Windows.Media;
using System.Diagnostics;

namespace Red.Toolkit.Profiler.Logic.Interpreters
{
    class PerfCounterTraceInterpreter : ProfilerDataView
    {
        private class PCEvent : Event
        {
            public PCEvent( PerfCounterTraceInterpreter parentView, RawEvent evt, Color color )
                : base( parentView, evt, color )
            {
            }

            public override float GetWeight()
            {
                return 1.0f;
            }

            public override string GetCaption()
            {
                return _Name;
            }

            public void SetData( uint level, uint channel, string name )
            {
                _Level = level;
                _Channel = channel;
                _Name = name;
            }

            private uint _Level;
            private uint _Channel;
            private string _Name;
        }

        public PerfCounterTraceInterpreter( ProfilerData data, ProfilerTraceInstance trace )
            : base( "Performance Counter Trace", data.FilePath, data, trace )
        {
            Style.SetColor( (int)DisplayType.Scope, Color.FromArgb( 255, 50, 100, 150 ) );
        }

        // display types
        private enum DisplayType
        {
            Selection = 0,
            Scope = 1,
            Invalid = -1
        };

        // general block types (must match c++)
        private enum BlockType
        {
            Scope = 1,
        };

        protected override bool AnalyzeRawData()
        {
            SortedDictionary<string, Color> colorsMap = new SortedDictionary<string, Color>();

            // analyze threads
            foreach ( var t in profilerData.Threads )
            {
                // empty or noise thread
                if ( t.Events.Count == 0 )
                    continue;

                // create group per thread
                string threadName = t.Name;
                Group threadGroup = AddGroup( threadName, true );

                // add events
                foreach ( var e in t.Events )
                {
                    var evt = e.Value;

                    // only blocks
                    if ( evt.Def.Type == ProfilerDefinitionType.Block )
                    {
                        // get emitted block type
                        BlockType blockType = (BlockType)evt.Def.ID;

                        if ( blockType == BlockType.Scope )
                        {
                            if ( evt.Type == RawEventType.BlockStart )
                            {
                                string name = profilerData.GetString( evt.Param( 0 ) );
                                if ( !colorsMap.ContainsKey( name ) )
                                {
                                    Color newColor = Utils.RandomColor( 0.25, 0.75, 0.25, 0.75 );
                                    colorsMap.Add( name, newColor );
                                }

                                Color color = colorsMap[name];

                                PCEvent scopeEvent = new PCEvent( this, evt, color );
                                scopeEvent.SetData( evt.Param( 1 ), evt.Param( 2 ), name );

                                AddEvent( scopeEvent, threadGroup );
                            }

                            continue;
                        }
                    }
                }
            }

            // done
            return true;
        }

        public override string GetDescription( IList<Event> events )
        {
            string txt = "";

            txt += string.Format( "Total perf. counter events: {0}\n", events.Count );

            SortedDictionary<string, List<PCEvent>> eventsByName = new SortedDictionary<string, List<PCEvent>>();

            foreach ( Event evt in events )
            {
                PCEvent pcEvent = evt as PCEvent;
                if ( !eventsByName.ContainsKey( pcEvent.Name ) )
                    eventsByName.Add( pcEvent.Name, new List<PCEvent>() );
                eventsByName[pcEvent.Name].Add( pcEvent );
            }

            foreach (var pair in eventsByName )
            {
                double totalTime = 0.0;
                foreach ( var evt in pair.Value )
                    totalTime += evt.GetLength();

                txt += string.Format( "- {0}: total time = {1}, avg. time = {2}, events = {3}\n", 
                    pair.Key,
                    Utils.FormatValue( totalTime, ValueType.Time ),
                    Utils.FormatValue( totalTime / pair.Value.Count, ValueType.Time ),
                    pair.Value.Count );
            }

            return txt;
        }

        public override string[] GetStatGraphsNames()
        {
            return null;
        }

        public override string[] GetBlockHitDataNames()
        {
            return new string[] { "File read blocks" };
        }
    }
}
